package com.theeviljames;

import java.util.Hashtable;

import com.theeviljames.lexer.Lexer;
import com.theeviljames.lexer.PGrammar;
import com.theeviljames.lexer.Token;
import com.theeviljames.maths.IMatrixOps;
import com.theeviljames.maths.MatrixOps;
import com.theeviljames.syntax.ASTNode;
import com.theeviljames.syntax.SyntaxAnalyser;
import com.theeviljames.syntax.exceptions.SyntaxException;

public class MatrixParser {

	//private MatrixManager matrixVariables;
	private Hashtable<String, ASTNode> expressions;
	//private static final byte ADD = 1,SUB = 2,MULT = 3,DIV = 4,POW = 5;
	private Hashtable<String, double[][]> matrixVariables;
	private static final IMatrixOps m = MatrixOps.getMatrixOps();
	
	public MatrixParser() {
		//matrixVariables = MatrixManager.getInstance();
		expressions = new Hashtable<String, ASTNode>();
	}
	
	public ASTNode parse(String expression) throws SyntaxException{
		Lexer lex = new Lexer(expression);
		Token t = lex.nextToken();
		String s = "";
		while(t.getType()!=Token.EOF){
			s = PGrammar.getGrammarString(t);
			t = lex.nextToken();
		}
		if(expressions.contains(s)){
			return expressions.get(s);
		}
		else{
			SyntaxAnalyser syn = new SyntaxAnalyser(expression);
			expressions.put(s, syn.parse());
			return expressions.get(s);
		}
	}
	
	public double[][] evaluate(String expression, Hashtable<String, double[][]> variables) throws SyntaxException{
		ASTNode ast = parse(expression);
		matrixVariables = variables;
		return eval(ast);
	}
	
	public double[][] eval(ASTNode node){
		int noOfChildNodes = node.getNodes().size();
		if(noOfChildNodes==0){
			if(node.getTokenType()==Token.CONSTANT){
				return new double[][]{{Double.valueOf(node.getLiteral())}};
			}
			else{
				return matrixVariables.get(node.getLiteral());
			}
		}
		else if(noOfChildNodes==1){
			return eval(node.getNodes().elementAt(0));
		}
		else if(noOfChildNodes==2){
			return m.negate(eval(node.getNodes().elementAt(1)));
		}
		else{
			if(node.getNodes().elementAt(0).getLiteral().equals("(")){
				return eval(node.getNodes().elementAt(1));
			}
			else{
				byte t = node.getNodes().elementAt(1).getTokenType();
				double[][] a = eval(node.getNodes().elementAt(0));
				double[][] b = eval(node.getNodes().elementAt(2));
				switch(t){
				case Token.ADD:
					if(a.length==1&&a[0].length==1)return m.scalarAdd(b, a[0][0]);
					if(b.length==1&&b[0].length==1)return m.scalarAdd(a, b[0][0]);
					return m.add(a, b);
				case Token.SUB:
					if(a.length==1&&a[0].length==1)return m.scalarSubtract(a[0][0], b);
					if(b.length==1&&b[0].length==1)return m.scalarSubtract(a, b[0][0]);
					return m.subtract(a, b);
				case Token.MULT:
					if(a.length==1&&a[0].length==1)return m.scalarMult(b, a[0][0]);
					if(b.length==1&&b[0].length==1)return m.scalarMult(a, b[0][0]);
					return m.mult(a, b);
				case Token.DIV:
					if(a.length==1&&a[0].length==1)return m.scalarDivide(a[0][0], b);
					if(b.length==1&&b[0].length==1)return m.scalarDivide(a, b[0][0]);
					return m.divide(a, b);
				case Token.POW:
					return m.pow(a,b[0][0]);
				}
			}
		}
		return null;
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try{
			String exp = "((X+X)-X*X)^4)";
			double[][] x = new double[][]{
					{3,0,0},
					{0,3,0},
					{0,0,3},
			};
			Hashtable<String, double[][]> variables = new Hashtable<String, double[][]>();
			variables.put("X", x);
			MatrixParser mp = new MatrixParser();
			m.print(mp.evaluate(exp,variables));
			//SyntaxAnalyser syn = new SyntaxAnalyser("(X*X+X)^2");
			//ASTNode ast = syn.parse();
			//ast.print();
		}
		catch(Exception e){
			e.printStackTrace();
		}
		
	}

}
